package com.jiangyg.mall.authz.support.authentication.member;

import com.jiangyg.mall.authz.constant.AuthzConstant;
import com.jiangyg.mall.authz.constant.SecurityConstant;
import com.jiangyg.mall.authz.MemberInfo;
import com.jiangyg.mall.authz.dto.MemberLoginParams;
import com.jiangyg.mall.authz.support.authentication.exception.CaptchaException;
import com.jiangyg.mall.authz.support.authentication.exception.UnknowException;
import com.jiangyg.mall.core.support.restful.Result;
import com.jiangyg.mall.core.utils.JsonUtils;
import com.jiangyg.mall.core.utils.Logger;
import com.jiangyg.mall.core.utils.WebUtils;
import com.jiangyg.mall.core.utils.bean.BeanUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 类描述：门户网站（会员）登录认证
 *
 * @author jiangyg
 * @version 4.0
 * @date 2021-10-09
 */
@Slf4j
public class MemberLoginAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    /**
     * 验证管理器
     */
    private final AuthenticationManager authenticationManager;

    public MemberLoginAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.setPostOnly(false);
        this.authenticationManager = authenticationManager;
        AntPathRequestMatcher matcher = new AntPathRequestMatcher(SecurityConstant.LOGIN_URL);
        this.setRequiresAuthenticationRequestMatcher(matcher);
    }

    /**
     * 功能描述：验证提交的用户信息
     *
     * @param request  请求
     * @param response 响应
     * @return 验证信息
     * @throws AuthenticationException 认证异常
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            // 解析登录信息
            final String body = WebUtils.readBodyToString(request);
            if (StringUtils.isBlank(body)) {
                throw new BadCredentialsException("未在请求体中获取到认证信息！");
            }
            final MemberLoginParams member = JsonUtils.parseObject(body, MemberLoginParams.class);
            // 账户密码验证
            Logger.info(log, () -> String.format("登录认证：开始进行用户[%s]的登录认证处理", member.getAccount()));
            final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(member.getAccount(), member.getPassword());
            return authenticationManager.authenticate(authentication);
        } catch (AuthenticationException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new UnknowException("用户登录认证异常：", ex);
        }
    }

    /**
     * 功能描述：认证成功调用的方法
     *
     * @param request    请求
     * @param response   响应
     * @param chain      过滤器链
     * @param authResult 认证结果
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain, Authentication authResult) {
        try {
            // 1. 认证成功，得到认证成功之后用户信息
            final MemberUserDetails principal = (MemberUserDetails) authResult.getPrincipal();
            final MemberInfo member = BeanUtils.transform(principal, MemberInfo.class);
            // 2. 设置登录会话
            request.getSession().setAttribute(AuthzConstant.MEMBER_INFO_SESSION_ID, member);
            // 3. 认证信息返回
            WebUtils.out(response, Result.ok(member));
        } catch (Exception ex) {
            Logger.error(log, () -> String.format("用户[%s]登录成功后处理失败", JsonUtils.toJSONString(authResult.getPrincipal())), ex);
        }
    }

    /**
     * 功能描述：认证失败处理
     *
     * @param request  请求
     * @param response 响应
     * @param failed   失败
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
        // 账户或者密码错误
        if (failed instanceof BadCredentialsException) {
            WebUtils.out(response, Result.instance("430"));
            return;
        }
        // 验证码异常
        if (failed instanceof CaptchaException) {
            WebUtils.out(response, Result.instance("434"));
            return;
        }
        // 账户过期
        if (failed instanceof AccountExpiredException) {
            WebUtils.out(response, Result.instance("431"));
            return;
        }
        // 账户状态异常
        if (failed instanceof AccountStatusException) {
            WebUtils.out(response, Result.instance("432"));
            return;
        }
        // 账户锁定异常
        if (failed instanceof LockedException) {
            WebUtils.out(response, Result.instance("433"));
            return;
        }
        Logger.error(log, () -> "登录认证异常：", failed);
        WebUtils.out(response, Result.instance("439"));
    }

}
